﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;

namespace DbHelper
{
    /// <summary>
    /// Classe helper para acesso à banco de dados.
    /// </summary>
    /// <remarks>Lucas Damiani</remarks>
    public abstract class Db
    {
        #region Propriedades
        /// <summary>
        /// String de conexão.
        /// </summary>
        /// <remarks>Lucas Damiani</remarks>
        public string ConnectionString { get; set; }

        /// <summary>
        /// Query utilizada para buscar a data/hora atual no banco de dados.
        /// </summary>
        /// <remarks>Lucas Damiani</remarks>
        public abstract string QueryDatetime { get; }
        #endregion

        #region Construtores
        /// <summary>
        /// Construtor default de DbHelper.
        /// </summary>
        /// <remarks>Lucas Damiani</remarks>
        public Db()
        {
        }

        /// <summary>
        /// Construtor base para todas as implementações do helper.
        /// </summary>
        /// <param name="connectionString">String de conexão.</param>
        /// <remarks>Lucas Damiani</remarks>
        public Db(string connectionString)
        {
            ConnectionString = connectionString;
        }
        #endregion

        #region Métodos
        /// <summary>
        /// Busca a data e hora atual no banco de dados.
        /// </summary>
        /// <returns>Objeto DateTime com a data e hora atual.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public DateTime GetDate()
        {
            return Convert.ToDateTime(ExecuteScalar(CommandType.Text, QueryDatetime, null));
        }

        /// <summary>
        /// Método utilizado para buscar uma conexão com o banco de dados.
        /// </summary>
        /// <returns>Conexão com o banco de dados.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public abstract DbConnection GetConnection();

        /// <summary>
        /// Método utilizado para buscar uma instância de DbParameter.
        /// </summary>
        /// <param name="name">Nome do parâmetro.</param>
        /// <param name="dbType">Tipo do parâmetro.</param>
        /// <param name="value">Valor que o parâmetro recebe. Se fornecido como "null", coloca o valor "DBNull.Value".</param>
        /// <returns>Instância de DbParameter de acordo com o banco de dados.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public abstract DbParameter GetParameter(string name, DbType dbType, object value);

        /// <summary>
        /// Método utilizado para buscar uma instância de DbParameter.
        /// </summary>
        /// <param name="name">Nome do parâmetro.</param>
        /// <param name="dbType">Tipo do parâmetro.</param>
        /// <param name="value">Valor que o parâmetro recebe. Se fornecido como "null", coloca o valor "DBNull.Value".</param>
        /// <param name="size">Tamanho do parâmetro no banco de dados.</param>
        /// <returns>Instância de DbParameter de acordo com o banco de dados.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public DbParameter GetParameter(string name, DbType dbType, object value, int size)
        {
            DbParameter param = GetParameter(name, dbType, value);
            param.Size = size;
            return param;
        }

        /// <summary>
        /// Método utilizado para executar um comando de escrita no banco de dados.
        /// </summary>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>Inteiro com o resultado do método original.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public abstract int ExecuteNonQuery(CommandType cmdType, string cmdText, List<DbParameter> cmdParms);

        /// <summary>
        /// Método utilizado para executar um comando de escrita no banco de dados, dentro de uma transação.
        /// </summary>
        /// <param name="trans">Transação corrente.</param>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>Inteiro com o resultado do método original.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public abstract int ExecuteNonQuery(DbTransaction trans, CommandType cmdType, string cmdText, List<DbParameter> cmdParms);

        /// <summary>
        /// Método utilizado para executar um comando de escrita no banco de dados.
        /// </summary>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>Comando SQL executado.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public abstract DbCommand ExecuteNonQueryCmd(CommandType cmdType, string cmdText, List<DbParameter> cmdParms);

        /// <summary>
        /// Método utilizado para executar um comando de escrita no banco de dados, dentro de uma transação.
        /// </summary>
        /// <param name="trans">Transação corrente.</param>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>Comando SQL executado.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public abstract DbCommand ExecuteNonQueryCmd(DbTransaction trans, CommandType cmdType, string cmdText, List<DbParameter> cmdParms);

        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados.
        /// </summary>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>DbDataReader de retorno da consulta.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public abstract DbDataReader ExecuteReader(CommandType cmdType, string cmdText, List<DbParameter> cmdParms);

        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados, dentro de uma transação.
        /// </summary>
        /// <param name="trans">Transação corrente.</param>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>DbDataReader de retorno da consulta.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public abstract DbDataReader ExecuteReader(DbTransaction trans, CommandType cmdType, string cmdText, List<DbParameter> cmdParms);

        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados.
        /// </summary>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>DataSet de retorno da consulta.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public abstract DataSet ExecuteReaderDs(CommandType cmdType, string cmdText, List<DbParameter> cmdParms);

        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados, dentro de uma transação.
        /// </summary>
        /// <param name="trans">Transação corrente.</param>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>DataSet de retorno da consulta.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public abstract DataSet ExecuteReaderDs(DbTransaction trans, CommandType cmdType, string cmdText, List<DbParameter> cmdParms);

        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados.
        /// </summary>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>Objeto de retorno da consulta.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public abstract object ExecuteScalar(CommandType cmdType, string cmdText, List<DbParameter> cmdParms);

        /// <summary>
        /// Método utilizado para executar um comando de leitura no banco de dados, dentro de uma transação.
        /// </summary>
        /// <param name="trans">Transação corrente.</param>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da Stored Procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <returns>Objeto de retorno da consulta.</returns>
        /// <remarks>Lucas Damiani</remarks>
        public abstract object ExecuteScalar(DbTransaction trans, CommandType cmdType, string cmdText, List<DbParameter> cmdParms);

        /// <summary>
        /// Método utilizado para preparar os comandos SQL que serão executados no banco.
        /// </summary>
        /// <param name="cmd">Comando SQL.</param>
        /// <param name="conn">Conexão que será utilizada.</param>
        /// <param name="trans">Transação corrente.</param>
        /// <param name="cmdType">Tipo do comando.</param>
        /// <param name="cmdText">Query ou nome da stored procedure.</param>
        /// <param name="cmdParms">Coleção de parâmetros SQL.</param>
        /// <remarks>Lucas Damiani</remarks>
        protected void PrepareCommand(DbCommand cmd, DbConnection conn, DbTransaction trans, CommandType cmdType, string cmdText, List<DbParameter> cmdParms)
        {
            if (conn.State != ConnectionState.Open)
                conn.Open();

            cmd.Connection = conn;
            cmd.CommandText = cmdText;

            if (trans != null)
                cmd.Transaction = trans;

            cmd.CommandType = cmdType;

            if (cmdParms != null)
            {
                foreach (DbParameter parm in cmdParms)
                {
                    if (cmd.Parameters.Contains(parm))
                        cmd.Parameters[parm.ParameterName] = parm;
                    else
                        cmd.Parameters.Add(parm);
                }
            }
        }
        #endregion
    }
}
